﻿using Engine;
using Engine.Audio;
using Engine.Graphics;
using Engine.Media;
using Game;
using GameEntitySystem;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Xml.Linq;
using TemplatesDatabase;
using XmlUtilities;

namespace Game
{
    /// <summary>
    /// 命令方块行为
    /// </summary>
    public class SubsystemCommandBlockBehavior : SubsystemBlockBehavior, IDrawable
    {
        class Chartlet
        {
            public Vector3 Position;
            public Vector3 P1;
            public Vector3 P2;
            public Vector3 P3;
            public Vector3 P4;
            public Color Color;
            public WorkingMode WorkingMode;
        }

        public override int[] HandledBlocks => new int[1] { CommandBlock.Index };

        private Dictionary<Point3, CommandData> m_commandDatas = new Dictionary<Point3, CommandData>();

        private Dictionary<Point3, Chartlet[]> m_chartlets = new Dictionary<Point3, Chartlet[]>();

        private PrimitivesRenderer3D m_primitivesRenderer = new PrimitivesRenderer3D();

        private TexturedBatch3D[] m_batchesByType = new TexturedBatch3D[4];

        private TexturedBatch3D[] m_batchesByType2 = new TexturedBatch3D[4];

        public SubsystemSky m_subsystemSky;

        public SubsystemTerrain m_subsystemTerrain;

        public Action<CommandData> OnCommandBlockGenerated;

        public static int[] m_drawOrders = new int[1] { 110 };

        public int[] DrawOrders => m_drawOrders;

        public override void Load(ValuesDictionary valuesDictionary)
        {
            CommandEditWidget.ScrollPosition.Clear();
            foreach (ValuesDictionary value in valuesDictionary.GetValue<ValuesDictionary>("Commands").Values)
            {
                Point3 position = value.GetValue<Point3>("Position");
                string line = value.GetValue("Line", "");
                SetCommandData(position, line);
            }
            m_subsystemSky = base.Project.FindSubsystem<SubsystemSky>(true);
            m_subsystemTerrain = base.Project.FindSubsystem<SubsystemTerrain>(true);
            m_batchesByType[0] = m_primitivesRenderer.TexturedBatch(ContentManager.Get<Texture2D>("Textures/Csharp0"), false, 0, DepthStencilState.DepthRead, RasterizerState.CullCounterClockwiseScissor, BlendState.AlphaBlend, SamplerState.LinearClamp);
            m_batchesByType[1] = m_primitivesRenderer.TexturedBatch(ContentManager.Get<Texture2D>("Textures/Csharp0"), false, 0, DepthStencilState.DepthRead, RasterizerState.CullCounterClockwiseScissor, BlendState.AlphaBlend, SamplerState.LinearClamp);
            m_batchesByType[2] = m_primitivesRenderer.TexturedBatch(ContentManager.Get<Texture2D>("Textures/Csharp0"), false, 0, DepthStencilState.DepthRead, RasterizerState.CullCounterClockwiseScissor, BlendState.AlphaBlend, SamplerState.LinearClamp);
            m_batchesByType[3] = m_primitivesRenderer.TexturedBatch(ContentManager.Get<Texture2D>("Textures/Csharp0"), false, 0, DepthStencilState.DepthRead, RasterizerState.CullCounterClockwiseScissor, BlendState.AlphaBlend, SamplerState.LinearClamp);
            m_batchesByType2[0] = m_primitivesRenderer.TexturedBatch(ContentManager.Get<Texture2D>("Textures/Csharp1"), false, 0, DepthStencilState.DepthRead, RasterizerState.CullCounterClockwiseScissor, BlendState.AlphaBlend, SamplerState.LinearClamp);
            m_batchesByType2[1] = m_primitivesRenderer.TexturedBatch(ContentManager.Get<Texture2D>("Textures/Csharp2"), false, 0, DepthStencilState.DepthRead, RasterizerState.CullCounterClockwiseScissor, BlendState.AlphaBlend, SamplerState.LinearClamp);
            m_batchesByType2[2] = m_primitivesRenderer.TexturedBatch(ContentManager.Get<Texture2D>("Textures/Csharp3"), false, 0, DepthStencilState.DepthRead, RasterizerState.CullCounterClockwiseScissor, BlendState.AlphaBlend, SamplerState.LinearClamp);
            m_batchesByType2[3] = m_primitivesRenderer.TexturedBatch(ContentManager.Get<Texture2D>("Textures/Csharp4"), false, 0, DepthStencilState.DepthRead, RasterizerState.CullCounterClockwiseScissor, BlendState.AlphaBlend, SamplerState.LinearClamp);
        }

        public override void Save(ValuesDictionary valuesDictionary)
        {
            int num = 0;
            ValuesDictionary vd = new ValuesDictionary();
            valuesDictionary.SetValue("Commands", vd);
            foreach (CommandData commandData in m_commandDatas.Values)
            {
                ValuesDictionary vd2 = new ValuesDictionary();
                vd2.SetValue("Position", commandData.Position);
                vd2.SetValue("Line", commandData.Line);
                vd.SetValue(num.ToString(CultureInfo.InvariantCulture), vd2);
                num++;
            }
        }

        public override void Dispose()
        {
            OnCommandBlockGenerated = null;
            try
            {
                CommandConfManager.SaveWhenDispose();
                InstructionManager.SaveHistoryItems();
            }
            catch (Exception e)
            {
                Log.Warning("CommandBlockBehaviorDispose:" + e.Message);
            }
        }

        public override bool OnInteract(TerrainRaycastResult raycastResult, ComponentMiner componentMiner)
        {
            if (!SubsystemCommand.InteractEnable) return false;
            AudioManager.PlaySound("Audio/UI/ButtonClick", 1f, 0f, 0f);
            Point3 position = new Point3(raycastResult.CellFace.X, raycastResult.CellFace.Y, raycastResult.CellFace.Z);
            if (componentMiner.ComponentPlayer != null)
            {
                componentMiner.ComponentPlayer.ComponentGui.ModalPanelWidget = new CommandEditWidget(this.Project, componentMiner.ComponentPlayer, position);
                return true;
            }
            return false;
        }

        public override void OnBlockModified(int value, int oldValue, int x, int y, int z)
        {
            Point3 position = new Point3(x, y, z);
            if (m_chartlets.TryGetValue(position, out Chartlet[] fourChartlet))
            {
                foreach (Chartlet chartlet in fourChartlet)
                {
                    int? colorValue = CommandBlock.GetColor(Terrain.ExtractData(value));
                    chartlet.Color = colorValue.HasValue ? DataHandle.GetCommandColor(colorValue.Value) : Color.Green;
                    chartlet.WorkingMode = CommandBlock.GetWorkingMode(value);
                }
            }
        }

        public override void OnBlockGenerated(int value, int x, int y, int z, bool isLoaded)
        {
            Point3 position = new Point3(x, y, z);
            CommandData commandData = GetCommandData(position);
            if (commandData == null)
            {
                commandData = SetCommandData(position, "");
            }
            if (OnCommandBlockGenerated != null)
            {
                OnCommandBlockGenerated.Invoke(commandData);
            }
            int? colorValue = CommandBlock.GetColor(Terrain.ExtractData(value));
            Color color = colorValue.HasValue ? DataHandle.GetCommandColor(colorValue.Value) : Color.Green;
            Chartlet[] fourChartlet = new Chartlet[4];
            for (int i = 0; i < fourChartlet.Length; i++)
            {
                Chartlet chartlet = new Chartlet();
                Vector3 vector = Vector3.Cross(CellFace.FaceToVector3(i), Vector3.UnitY);
                chartlet.Position = new Vector3(position) + new Vector3(0.5f) + 0.5075f * CellFace.FaceToVector3(i);
                chartlet.P1 = chartlet.Position - 0.52f * (vector + Vector3.UnitY);
                chartlet.P2 = chartlet.Position + 0.52f * (vector - Vector3.UnitY);
                chartlet.P3 = chartlet.Position + 0.52f * (vector + Vector3.UnitY);
                chartlet.P4 = chartlet.Position - 0.52f * (vector - Vector3.UnitY);
                chartlet.Color = color;
                chartlet.WorkingMode = CommandBlock.GetWorkingMode(value);
                fourChartlet[i] = chartlet;
            }
            m_chartlets[position] = fourChartlet;
        }

        public override void OnBlockAdded(int value, int oldValue, int x, int y, int z)
        {
            OnBlockGenerated(value, x, y, z, false);
        }

        public override void OnBlockRemoved(int value, int newValue, int x, int y, int z)
        {
            Point3 position = new Point3(x, y, z);
            m_commandDatas.Remove(position);
            m_chartlets.Remove(position);
        }

        public CommandData GetCommandData(Point3 position)
        {
            if (m_commandDatas.TryGetValue(position, out CommandData value))
            {
                return value;
            }
            return null;
        }

        public CommandData SetCommandData(Point3 position, string line)
        {
            CommandData commandData = new CommandData(position, line);
            string result = commandData.TrySetValue();
            if (commandData.Name == null)
            {
                commandData.Line = string.Empty;
            }
            //else
            //{
            //	if (InstructionManager.GetInstruction(commandData.Name, false) == null)
            //	{
            //		if (InstructionManager.GetInstruction(commandData.Name, true) == null)
            //		{
            //			commandData.Line = string.Empty;
            //		}
            //	}
            //}
            base.Project.FindSubsystem<SubsystemCommand>(true).ShowEditedTips(result);
            m_commandDatas[position] = commandData;
            return commandData;
        }

        public void Draw(Camera camera, int drawOrder)
        {
            if (!SubsystemCommand.ChartletDraw) return;
            foreach (Point3 point3 in m_chartlets.Keys)
            {
                for (int i = 0; i < m_chartlets[point3].Length; i++)
                {
                    Chartlet chartlet = m_chartlets[point3][i];
                    if (chartlet.Color.A > 0)
                    {
                        Vector3 vector = chartlet.Position - camera.ViewPosition;
                        float num = Vector3.Dot(vector, camera.ViewDirection);
                        Vector3 v = 0.03f * num / vector.Length() * vector;
                        if (num > 0.01f && vector.Length() < m_subsystemSky.ViewFogRange.Y)
                        {
                            if (chartlet.WorkingMode != WorkingMode.Variable)
                            {
                                m_batchesByType[i].QueueQuad(chartlet.P1 - v, chartlet.P2 - v, chartlet.P3 - v, chartlet.P4 - v, new Vector2(0f, 0f), new Vector2(1f, 0f), new Vector2(1f, 1f), new Vector2(0f, 1f), chartlet.Color);
                            }
                            else
                            {
                                m_batchesByType2[i].QueueQuad(chartlet.P1 - v, chartlet.P2 - v, chartlet.P3 - v, chartlet.P4 - v, new Vector2(0f, 0f), new Vector2(1f, 0f), new Vector2(1f, 1f), new Vector2(0f, 1f), chartlet.Color);
                            }
                        }
                    }
                }
            }
            m_primitivesRenderer.Flush(camera.ViewProjectionMatrix);
        }
    }
}